package org.intellij.doctree;

import com.intellij.ui.DocumentAdapter;
import com.intellij.util.net.HttpConfigurable;
import org.intellij.doctree.node.Document;
import org.intellij.doctree.node.DocumentNode;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.ChangedCharSetException;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.parser.ParserDelegator;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DocumentDialog extends NodeDialog {

    private static final String HTTP_PREFIX = "http:";

    protected JTextField docName;
    protected JTextField docUrl;
    protected JTextField docDesc;
    protected JButton urlButton;

    public DocumentDialog(Component parent) throws HeadlessException {
        super(parent);
        newNode = true;
        init();
    }

    public DocumentDialog(Component parent, DocumentNode docNode) throws HeadlessException {
        super(parent);
        init();
        setDocument(docNode.getDocument());
    }

    protected void init() {
        super.init();
        setTitle(newNode ? "New Document" : "Edit Document");
        pack();
    }

    @Nullable
    protected JComponent createCenterPanel() {

        JPanel fieldPanel = new JPanel();

        JLabel docNameLbl = new JLabel("Document Name:");
        JLabel docURLLbl = new JLabel("URL:");
        JLabel docDescLbl = new JLabel("Description:");
        docName = new JTextField("");
        docName.setColumns(32);
        docUrl = new JTextField("");
        docUrl.setColumns(32);
        docUrl.getDocument().addDocumentListener(new DocumentAdapter() {
            protected void textChanged(DocumentEvent e) {
                String url = docUrl.getText().trim();
                urlButton.setEnabled(url.startsWith(HTTP_PREFIX));
            }
        });
        docDesc = new JTextField("");
        docDesc.setColumns(32);
        urlButton = new JButton("?");
        urlButton.setToolTipText("Check URL");
        urlButton.setMargin(new Insets(2, 2, 2, 2));
        urlButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doUrlButtonAction();
            }
        });

        fieldPanel.setLayout(new GridBagLayout());
        GridBagConstraints c;

        c = new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0);
        fieldPanel.add(docNameLbl, c);

        c = new GridBagConstraints(1, 0, 2, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0);
        fieldPanel.add(docName, c);

        c = new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0);
        fieldPanel.add(docURLLbl, c);

        c = new GridBagConstraints(1, 1, 1, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0);
        fieldPanel.add(docUrl, c);

        c = new GridBagConstraints(2, 1, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0);
        fieldPanel.add(urlButton, c);

        c = new GridBagConstraints(0, 2, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0);
        fieldPanel.add(docDescLbl, c);

        c = new GridBagConstraints(1, 2, 2, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0);
        fieldPanel.add(docDesc, c);

        c = new GridBagConstraints(0, 3, 3, 1, 1, 1, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 0, 0);
        fieldPanel.add(new JPanel(), c);

        return fieldPanel;

    }

    public String getDocUrl() {
        return docUrl.getText();
    }

    public String getDocName() {
        return docName.getText();
    }

    public String getDocDesc() {
        return docDesc.getText();
    }

    public Document getValue() {
        return getDocument();
    }

    protected boolean isValidated() {
        String url = docUrl.getText().trim();
        if (url.length() == 0) {
            JOptionPane.showMessageDialog(this.getWindow(), "Document URL cannot be blank",
                    "Document Details", JOptionPane.ERROR_MESSAGE);
            docUrl.requestFocus();
            return false;
        }
        String name = docName.getText().trim();
        if (name.length() == 0) {
            docName.setText(docUrl.getText().trim());
        }
        return true;
    }

    public Document getDocument() {
        return new Document(docUrl.getText(), docName.getText(), docDesc.getText());
    }

    public void setDocument(Document doc) {
        newNode = false;
        docUrl.setText(doc.getURL());
        docName.setText(doc.getName());
        docDesc.setText(doc.getDescription());
    }

    public JComponent getPreferredFocusedComponent() {
        return docName;
    }

    private void doUrlButtonAction() {
        String url = docUrl.getText().trim();
        if (url.length() == 0) return;

        if (url.startsWith(HTTP_PREFIX)) {

            try {
                HttpConfigurable.getInstance().prepareURL(url);
                URL docUrl = new URL(url);
                    String title = getTitleFromStream(docUrl);
                    if (title != null) docName.setText(title);
            } catch (MalformedURLException e) {
                JOptionPane.showMessageDialog(this.getWindow(), "Invalid URL '" + url + "'",
                        "Check URL", JOptionPane.ERROR_MESSAGE);
                return;
            } catch (IOException e) {
                JOptionPane.showMessageDialog(this.getWindow(), "Unable to read from URL '" + url + "'",
                        "Check URL", JOptionPane.ERROR_MESSAGE);
                return;
            }

        }

        JOptionPane.showMessageDialog(this.getWindow(), "URL is OK",
                "Check URL", JOptionPane.INFORMATION_MESSAGE);
    }

    private String getTitleFromStream(URL docUrl) throws IOException {

        final ParserDelegator pd = new ParserDelegator();
        final HTMLCallback callback = new HTMLCallback();
        InputStreamReader reader = null;

        try {
            //  First attempt to read using ISO-8859-1.  If the charset is
            //  later specified in a <META http-equiv="Content-Type" ...>
            //  tag, ParserDelegator will throw ChangedCharSetException,
            //  which is caught below and handled.

            Object content = docUrl.getContent();
            if (!(content instanceof InputStream)) return null;
            InputStream inputStream = (InputStream) content;
            reader = new InputStreamReader(inputStream, "ISO-8859-1");
            pd.parse(reader, callback, false);


        } catch (ChangedCharSetException e) {
            //  This means the HTML file specifies a charset, so the entire
            //  file needs to be reparsed using that charset.  The code
            //  below uses a regexp pattern to pick out the charset name.
            //
            //  The syntax grammar for the Content-Type header is specified
            //  in section 5.1 of RFC 2045
            //  http://www.ietf.org/rfc/rfc2045.txt
            final String spec = e.getCharSetSpec();
            final Pattern p = Pattern.compile("charset=\"?(.+)\"?\\s*;?",
                    Pattern.CASE_INSENSITIVE);
            final Matcher m = p.matcher(spec);
            final String charset = m.find() ? m.group(1) : "ISO-8859-1";  //NOTRANS
            try {

                Object content = docUrl.getContent();
                if (!(content instanceof InputStream)) return null;
                InputStream inputStream = (InputStream) content;
                reader = new InputStreamReader(inputStream, charset);

                callback.inTitle = false;
                pd.parse(reader, callback, true);

            } catch (Throwable t) {
                //ignore
            } finally {
                if (reader != null) try {
                    reader.close();
                } catch (Exception ex) {/*ignore */}
            }
        } catch (Throwable t) {
            //ignore
        }
        finally {
            if (reader != null) try {
                reader.close();
            } catch (Exception ex) {/*ignore */}
        }

        return callback.title;

    }

    public void setURL(String url) {
        if (url == null) url = "";
        docUrl.setText(url);
    }

    /**
     * SAX-style callback for the Swing HTML parser.
     */
    class HTMLCallback extends HTMLEditorKit.ParserCallback {

        private boolean inTitle;
        private String title;

        public void handleText(char[] data, int pos) {
            if (inTitle) {
                title = new String(data);
            }
        }

        public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
            if (t == HTML.Tag.TITLE) {
                inTitle = true;
            }
        }

        public void handleEndTag(HTML.Tag t, int pos) {
            if (t == HTML.Tag.TITLE) {
                inTitle = false;
            }
        }

        public String getTitle() {
            return title;
        }

    }

}